/* 
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */


/****
 *  GLOBAL VARIABLES
 */
var map;
var markerCluster;
var zoom = 12;
var cityCircle;

var trainingMarkers = [];
var testingMarkers = [];
var predictionMarkers = [];
var murderMarkers = [];
var rapeMarkers = [];
var robberyMarkers = [];
var assaultMarkers = [];
var burglaryMarkers = [];
var larcenyTheftMarkers = [];
var carTheftMarkers = [];

var showTraining;
var showTesting;
var showPrediction;
var murder;
var rape;
var robbery;
var assault;
var burglary;
var larcenytheft;
var cartheft;

var trainingData = [];
var testingData = [];
var predictionData = [];

var totalTraining = 17;
var totalTesting = 13;

var k = totalTraining * 0.1;

var clusters = [];

var crimeTypes = ["Murder", "Rape", "Robbery",
    "Assault", "Burglary", "Larceny Theft",
    "Car Theft"];

var icons = ["markers/red_MarkerM.png", "markers/orange_MarkerR.png", "markers/yellow_MarkerR.png",
    "markers/blue_MarkerA.png", "markers/brown_MarkerB.png", "markers/darkgreen_MarkerL.png",
    "markers/paleblue_MarkerC.png"];

var today = new Date();

// One hour in millisecond
var oneHour = 60 * 60 * 1000;

var sanJose = new google.maps.LatLng(37.33664890, -121.88033940);

var citymap = {};
citymap['chicago'] = {
    center: new google.maps.LatLng(37.247275, -121.802788)
};
citymap['newyork'] = {
    center: new google.maps.LatLng(37.406165, -121.9421)
};
citymap['losangeles'] = {
    center: new google.maps.LatLng(37.230328, -121.913338)
}

var trainingReq;
var testingReq;
var predictingReq;

/**
 * FUNCTION DEFINITION
 */

/**
 * Add Home control to the map to navigate back to San Jose.
 * @param map
 */
function addHomeControl(map) {

    var control = document.createElement('DIV');
    control.innerHTML = 'Home';
    control.className = 'button';
    control.index = 1;

    // Add the control to the map
    map.controls[google.maps.ControlPosition.TOP_RIGHT].push(control);

    // When the button is clicked pan to sydney
    google.maps.event.addDomListener(control, 'click', function() {
        map.setCenter(sanJose);
        map.setZoom(zoom);
        control.className = control.className == 'button' ? 'button-selected' : 'button';
    });
}


/**
 * Add the Predict control to the map.
 * @param map
 */
//function addPredictControl(map) {
//
//    var control = document.createElement('DIV');
//    control.innerHTML = 'Predict';
//    control.className = 'button';
//    control.index = 1;
//
//    // Add the control to the map
//    map.controls[google.maps.ControlPosition.TOP_RIGHT].push(control);
//
//    // When the button is clicked pan
//    google.maps.event.addDomListener(control, 'click', function() {
//        predict();
//        control.className = control.className == 'button' ? 'button-selected' : 'button';
//    });
//}


/**
 * Add a marker to the map and push to the array.
 *
 * @param location
 * @param title
 */
function addMarker(location, title) {

    var marker = new google.maps.Marker({
        position: location,
        draggable: true,
        map: map,
        title: title
    });
    trainingMarkers.push(marker);
    markerCluster.addMarkers(trainingMarkers, true);
}


/**
 *
 * @returns {*}
 */
function generateCrimeType() {

    var randomIndex = generateRandomIndex();
    return types[randomIndex];
}


function generateMarker() {

    var latitude = generateLatitude();
    var longitude = generateLongitude();
    var center = new google.maps.LatLng(latitude, longitude);
    var randomIndex = generateRandomIndex();

    var marker = new google.maps.Marker({
        position: center,
        draggable: true,
        map: map,
        title: crimeTypes[randomIndex],
        icon: icons[randomIndex]
    });

    return marker;
}


function generatePredictionMarkers(total) {

    for (var i = 0; i < total; i++) {
        predictionMarkers.push(generateMarker());
    }

    markerCluster.addMarkers(predictionMarkers);
}


/**
 *
 * @param marker
 * @param index
 */
function addCrimeTypeMarker(marker, index) {

    switch (index) {
        case 0:
            murderMarkers.push(marker);
            break;
        case 1:
            rapeMarkers.push(marker);
            break;
        case 2:
            robberyMarkers.push(marker);
            break;
        case 3:
            assaultMarkers.push(marker);
            break;
        case 4:
            burglarygMarkers.push(marker);
            break;
        case 5:
            larcenyTheftMarkers.push(marker);
            break;
        case 6:
            carTheftMarkers.push(marker);
            break;
    }
}


/**
 * Generate a random latitude within San Jose.
 *
 * @returns {number}
 */
function generateLatitude() {

    var latitude = Math.random() * 0.204 + 37.228;
    return latitude;
}


/**
 * Generate a random longitude within San Jose.
 *
 * @returns {number}
 */
function generateLongitude() {

    var longitude = Math.random() * (-0.234) - 121.745;
    return longitude;
}


/**
 * Generate a random index to select a crime in a crime array.
 *
 * @returns {number}
 */
function generateRandomIndex() {

    var randomIndex = Math.floor(Math.random() * crimeTypes.length);
    return randomIndex;
}


/**
 * Generate a random number of suspects for training and testing data.
 *
 * @returns {number}
 */
function generateRandomSuspects() {

    var randomNumber = Math.floor(Math.random() * 10) + 1;
    return randomNumber;
}


/**
 * Generate random trainning data.
 *
 * @param total
 */
function generateTrainingData(total) {

    for (var i = 0; i < total; i++) {
        var latitude = generateLatitude();
        var longitude = generateLongitude();
        var center = new google.maps.LatLng(latitude, longitude);
        var randomIndex = generateRandomIndex();
        var preHour = today.getTime() - oneHour * i;

        var marker = new google.maps.Marker({
            position: center,
            draggable: true,
            map: map,
            title: crimeTypes[randomIndex],
            icon: icons[randomIndex]
        });

        trainingMarkers.push(marker);
//        addCrimeTypeMarker(marker, randomIndex);

        var data = new Object();
        data.latitude = latitude;
        data.longitude = longitude;
        data.type = crimeTypes[randomIndex];
        data.suspects = generateRandomSuspects();
        data.date = new Date(preHour);
        data.marker = marker;

        trainingData.push(data);
    }

    markerCluster.addMarkers(trainingMarkers);
//    markerCluster.addMarkers(murderMarkers);
//    markerCluster.addMarkers(rapeMarkers);
//    markerCluster.addMarkers(robberyMarkers);
//    markerCluster.addMarkers(assaultMarkers);
//    markerCluster.addMarkers(burglarygMarkers);
//    markerCluster.addMarkers(larcenyTheftMarkers);
//    markerCluster.addMarkers(carTheftMarkers);
}


/**
 * Generate random testing data.
 *
 * @param total
 */
function generateTestingData(total) {

    for (var i = 0; i < total; i++) {
        var latitude = generateLatitude();
        var longitude = generateLongitude();
        var center = new google.maps.LatLng(latitude, longitude);
        var randomIndex = generateRandomIndex();
        var preHour = today.getTime() - oneHour * i;

        var marker = new google.maps.Marker({
            position: center,
            draggable: true,
            map: map,
            title: crimeTypes[randomIndex],
            icon: icons[randomIndex]
        });

        testingMarkers.push(marker);
//        addCrimeTypeMarker(marker, randomIndex);

        var data = new Object();
        data.latitude = latitude;
        data.longitude = longitude;
        data.type = crimeTypes[randomIndex];
        data.suspects = generateRandomSuspects();
        data.date = new Date(preHour);
        data.marker = marker;

        testingData.push(data);
    }

    markerCluster.addMarkers(testingMarkers);
//    markerCluster.addMarkers(murderMarkers);
//    markerCluster.addMarkers(rapeMarkers);
//    markerCluster.addMarkers(robberyMarkers);
//    markerCluster.addMarkers(assaultMarkers);
//    markerCluster.addMarkers(burglarygMarkers);
//    markerCluster.addMarkers(larcenyTheftMarkers);
//    markerCluster.addMarkers(carTheftMarkers);
}


/**
 * Generate random prediction data.
 *
 * @param total
 */
function generatePredictionData(total) {

    for (var i = 0; i < total; i++) {
        var latitude = generateLatitude();
        var longitude = generateLongitude();
        var center = new google.maps.LatLng(latitude, longitude);
        var randomIndex = generateRandomIndex();
        var preHour = today.getTime() - oneHour * i;

        var marker = new google.maps.Marker({
            position: center,
            draggable: true,
            map: map,
            title: crimeTypes[randomIndex],
            icon: icons[randomIndex]
        });

        predictionMarkers.push(marker);
//        addCrimeTypeMarker(marker, randomIndex);

        var data = new Object();
        data.latitude = latitude;
        data.longitude = longitude;
        data.type = crimeTypes[randomIndex];
        data.suspects = generateRandomSuspects();
        data.date = new Date(preHour);
        data.marker = marker;

        predictionData.push(data);
    }

    markerCluster.addMarkers(predictionMarkers);
//    markerCluster.addMarkers(murderMarkers);
//    markerCluster.addMarkers(rapeMarkers);
//    markerCluster.addMarkers(robberyMarkers);
//    markerCluster.addMarkers(assaultMarkers);
//    markerCluster.addMarkers(burglarygMarkers);
//    markerCluster.addMarkers(larcenyTheftMarkers);
//    markerCluster.addMarkers(carTheftMarkers);
}


/**
 * Calculate distance between two points given their latitudes and longitudes.
 *
 * @param lat1
 * @param lon1
 * @param lat2
 * @param lon2
 * @returns {number}
 */
function getDistanceFromLatLonInKm(lat1, lon1, lat2, lon2) {

    var R = 6371; // Radius of the earth in km
    var dLat = deg2rad(lat2 - lat1);  // deg2rad below
    var dLon = deg2rad(lon2 - lon1);
    var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
            Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
            Math.sin(dLon / 2) * Math.sin(dLon / 2)
            ;
    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
    var d = R * c; // Distance in km
    return d;
}


/**
 * Convert degreee to radian.
 *
 * @param deg
 * @returns {number}
 */
function deg2rad(deg) {

    return deg * (Math.PI / 180)
}


/**
 * K-means clustering algorithm.
 *
 */
function kMeans() {

    var k = totalTraining * 0.1;

    // Create k clusters with their initial centroid
    for (var i = 0; i < k; i++) {
        var aCluster = new Object();
//        aCluster.la
    }
}


/**
 * Predict future crimes.
 *
 */
function predict() {
//    kMeans();
}


/**
 * K-means clustering algorithm.
 *
 */
function kMeans() {
    var iteration = 0;

    // Create k clusters with their initial centroid
    for (var i = 0; i < k; i++) {
        var aCluster = new Object();
        aCluster.latitude = generateLatitude();
        aCluster.longitude = generateLongitude();
        aCluster.members = [];

        clusters.push(aCluster);
    }

    var isNotConverged = true;
    while (isNotConverged) {

        iteration++;

        assignToClosestCentroid();

        // Exit loop condition
        isNotConverged = false;

        // Recompute the centroid in each cluster.
        // Repeat if not converged.
        for (var i = 0; i < k; i++) {

            var prevLat = clusters[i].latitude;
            var prevLong = clusters[i].longitude;

            var sumLat = 0;
            var sumLong = 0;

            // Array of data points.
            var members = clusters[i].members;
            var total = members.length;

            for (var j = 0; j < total; j++) {
                sumLat += members[j].latitude;
                sumLong += members[j].longitude;
            }

            // Compute the means.
            var newLat = sumLat / total;
            var newLong = sumLong / total;

            if (newLat != prevLat || newLong != prevLong) {
                clusters[i].latitude = newLat;
                clusters[i].longitude = newLong;

                if (isNotConverged == false) {
                    isNotConverged = true;
                }
            }
        }


    }
    alert("exit while. Iteration = " + iteration);
}

function assignToClosestCentroid() {
    // Assign each data point to the closest centroid.
    for (var i = 0; i < totalTraining; i++) {

        var minDist = getDistanceFromLatLonInKm(
                trainingData[i].latitude,
                trainingData[i].longitude,
                clusters[0].latitude,
                clusters[0].longitude);
        var minIndex = 0;

        for (var j = 0; j < k; j++) {

            var currentDist = getDistanceFromLatLonInKm(
                    trainingData[i].latitude,
                    trainingData[i].longitude,
                    clusters[j].latitude,
                    clusters[j].longitude
                    );

            if (currentDist < minDist) {
                minDist = currentDist;
                minIndex = j;
            }
        }

        // Assign current data point to the closes centroid.
        clusters[minIndex].members.push(trainingData[i]);
    }
}


/**
 * This initialize function will be called when the page is loaded.
 *
 */
function initialize() {

    var mapDiv = document.getElementById('map-canvas');
    var mapOptions = {
        zoom: zoom,
        center: sanJose,
        mapTypeControl: true,
        mapTypeControlOptions: {
            style: google.maps.MapTypeControlStyle.DROPDOWN_MENU
        },
        mapTypeId: google.maps.MapTypeId.ROADMAP
    }
    map = new google.maps.Map(mapDiv, mapOptions);

    // Add zoom clusterer
    var mcOptions = {gridSize: 50, maxZoom: 15};
    markerCluster = new MarkerClusterer(map, [], mcOptions);

    addHomeControl(map);

//    addPredictControl(map);

    // Add circles
//    for (var city in citymap) {
//        // Construct the circle for each value in citymap. We scale population by 20.
//        var populationOptions = {
//            strokeColor: '#FF0000',
//            strokeOpacity: 0.8,
//            strokeWeight: 2,
//            fillColor: '#00FFFF',
//            fillOpacity: 0.35,
//            map: map,
//            center: citymap[city].center,
//            radius: 500
//        };
//
//        cityCircle = new google.maps.Circle(populationOptions);
//    }

    showTraining = document.getElementById("show-training");
    showTesting = document.getElementById("show-testing");
    showPrediction = document.getElementById("show-prediction");

    murder = document.getElementById("murder");
    rape = document.getElementById("rape");
    robbery = document.getElementById("robbery");
    assault = document.getElementById("assault");
    burglary = document.getElementById("burglary");
    larcenytheft = document.getElementById("larcenytheft");
    cartheft = document.getElementById("cartheft");

    totalTraining = document.getElementById("total-training");
    totalTesting = document.getElementById("total-testing");

}


function toggleTraining() {
    if (showTraining.checked) {
        markerCluster.addMarkers(trainingMarkers);
    } else {
        markerCluster.removeMarkers(trainingMarkers);
    }
}


function toggleTesting() {
    if (showTesting.checked) {
        markerCluster.addMarkers(testingMarkers);
    } else {
        markerCluster.removeMarkers(testingMarkers);
    }
}


function togglePrediction() {
    if (showPrediction.checked) {
        markerCluster.addMarkers(predictionMarkers);
    } else {
        markerCluster.removeMarkers(predictionMarkers);
    }
}


function toggleMurder() {
    if (murder.checked) {
        markerCluster.addMarkers(murderMarkers);
    } else {
        markerCluster.removeMarkers(murderMarkersÏ);
    }
}


function toggleRape() {
    if (rape.checked) {
        markerCluster.addMarkers(rapeMarkers);
    } else {
        markerCluster.removeMarkers(rapeMarkers);
    }
}


function toggleRobbery() {
    if (robbery.checked) {
        markerCluster.addMarkers(robberyMarkers);
    } else {
        markerCluster.removeMarkers(robberyMarkers);
    }
}


function toggleAssault() {
    if (assault.checked) {
        markerCluster.addMarkers(assaultMarkers);
    } else {
        markerCluster.removeMarkers(assaultMarkers);
    }
}


function toggleBurglary() {
    if (burglary.checked) {
        markerCluster.addMarkers(burglaryMarkers);
    } else {
        markerCluster.removeMarkers(burglaryMarkers);
    }
}


function toggleLarcenyTheft() {
    if (larcenytheft.checked) {
        markerCluster.addMarkers(larcenyTheftMarkers);
    } else {
        markerCluster.removeMarkers(larcenyTheftMarkers);
    }
}


function toggleCarTheft() {
    if (cartheft.checked) {
        markerCluster.addMarkers(carTheftMarkers);
    } else {
        markerCluster.removeMarkers(carTheftMarkers);
    }
}


function removePredictionMarkers() {
    markerCluster.removeMarkers(murderMarkers);
    markerCluster.removeMarkers(rapeMarkers);
    markerCluster.removeMarkers(robberyMarkers);
    markerCluster.removeMarkers(assaultMarkers);
    markerCluster.removeMarkers(burglaryMarkers);
    markerCluster.removeMarkers(larcenyTheftMarkers);
    markerCluster.removeMarkers(carTheftMarkers);

    murderMarkers = [];
    rapeMarkers = [];
    robberyMarkers = [];
    assaultMarkers = [];
    burglaryMarkers = [];
    larcenyTheftMarkers = [];
    carTheftMarkers = [];
}


function predict() {

    removePredictionMarkers();

    var predictingUrl = "GenerateDataServlet?type=predicting";
    predictingReq = initRequest();
    predictingReq.open("GET", predictingUrl, true);
    predictingReq.onreadystatechange = predictingCallback;
    predictingReq.send(null);
}


function generateData() {

    removePredictionMarkers();

    markerCluster.removeMarkers(trainingMarkers);
    markerCluster.removeMarkers(testingMarkers);

    trainingMarkers = [];
    testingMarkers = [];

    var trainingUrl = "GenerateDataServlet?type=training&training=" + escape(totalTraining.value);
    trainingReq = initRequest();
    trainingReq.open("GET", trainingUrl, true);
    trainingReq.onreadystatechange = trainingCallback;
    trainingReq.send(null);

    var testingUrl = "GenerateDataServlet?type=testing&testing=" + escape(totalTesting.value);
    testingReq = initRequest();
    testingReq.open("GET", testingUrl, true);
    testingReq.onreadystatechange = testingCallback;
    testingReq.send(null);
}


function testingCallback() {
    if (testingReq.readyState == 4) {
        if (testingReq.status == 200) {
            parseTestingMessages(testingReq.responseXML);
        }
    }
}


function trainingCallback() {
    if (trainingReq.readyState == 4) {
        if (trainingReq.status == 200) {
            parseTrainingMessages(trainingReq.responseXML);
        }
    }
}


function predictingCallback() {
    if (predictingReq.readyState == 4) {
        if (predictingReq.status == 200) {
            parsePredictingMessages(predictingReq.responseXML);
        }
    }
}


function parseTrainingMessages(responseXML) {

    // no matches returned
    if (responseXML == null) {
        return false;
    }

    var training = responseXML.getElementsByTagName("training")[0];
    // no data retunred
    if (training.childNodes.length == 0) {
        return false;
    }

    for (loop = 0; loop < training.childNodes.length; loop++) {

        var crime = training.childNodes[loop];
        var latitudeElement = crime.getElementsByTagName("latitude")[0];
        var longitudeElement = crime.getElementsByTagName("longitude")[0];
        var typeElement = crime.getElementsByTagName("type")[0];
        var iconElement = crime.getElementsByTagName("icon")[0];

        var latitude = latitudeElement.childNodes[0].nodeValue;
        var longitude = longitudeElement.childNodes[0].nodeValue;
        var type = typeElement.childNodes[0].nodeValue;
        var icon = iconElement.childNodes[0].nodeValue;

        var center = new google.maps.LatLng(latitude, longitude);
        var marker = new google.maps.Marker({
            position: center,
            draggable: true,
            map: map,
            title: type,
            icon: icon
        });

        trainingMarkers.push(marker);
    }

    markerCluster.addMarkers(trainingMarkers);
}


function parseTestingMessages(responseXML) {

    // no matches returned
    if (responseXML == null) {
        return false;
    }

    var testing = responseXML.getElementsByTagName("testing")[0];
    // no data retunred
    if (testing.childNodes.length == 0) {
        return false;
    }

    for (loop = 0; loop < testing.childNodes.length; loop++) {

        var crime = testing.childNodes[loop];
        var latitudeElement = crime.getElementsByTagName("latitude")[0];
        var longitudeElement = crime.getElementsByTagName("longitude")[0];
        var typeElement = crime.getElementsByTagName("type")[0];
        var iconElement = crime.getElementsByTagName("icon")[0];

        var latitude = latitudeElement.childNodes[0].nodeValue;
        var longitude = longitudeElement.childNodes[0].nodeValue;
        var type = typeElement.childNodes[0].nodeValue;
        var icon = iconElement.childNodes[0].nodeValue;

        var center = new google.maps.LatLng(latitude, longitude);
        var marker = new google.maps.Marker({
            position: center,
            draggable: true,
            map: map,
            title: type,
            icon: icon
        });

        testingMarkers.push(marker);
    }

    markerCluster.addMarkers(testingMarkers);
}



function parsePredictingMessages(responseXML) {

    // no matches returned
    if (responseXML == null) {
        return false;
    }

    var predicting = responseXML.getElementsByTagName("predicting")[0];
    // no data retunred
    if (predicting.childNodes.length == 0) {
        return false;
    }

    for (loop = 0; loop < predicting.childNodes.length; loop++) {

        var crime = predicting.childNodes[loop];
        var latitudeElement = crime.getElementsByTagName("latitude")[0];
        var longitudeElement = crime.getElementsByTagName("longitude")[0];
        var typeElement = crime.getElementsByTagName("type")[0];
        var iconElement = crime.getElementsByTagName("icon")[0];

        var latitude = latitudeElement.childNodes[0].nodeValue;
        var longitude = longitudeElement.childNodes[0].nodeValue;
        var type = typeElement.childNodes[0].nodeValue;
        var icon = iconElement.childNodes[0].nodeValue;

        var center = new google.maps.LatLng(latitude, longitude);
        var marker = new google.maps.Marker({
            position: center,
            draggable: true,
            map: map,
            title: type,
            icon: icon
        });


        if (type == "Murder") {
            murderMarkers.push(marker);

        } else if (type == "Rape") {
            rapeMarkers.push(marker);

        } else if (type == "Robbery") {
            robberyMarkers.push(marker);

        } else if (type == "Assault") {
            assaultMarkers.push(marker);

        } else if (type == "Burglary") {
            burglaryMarkers.push(marker);

        } else if (type == "Larceny Theft") {
            larcenyTheftMarkers.push(marker);

        } else if (type == "Car Theft") {
            carTheftMarkers.push(marker);
        }
    }

    markerCluster.addMarkers(murderMarkers);
    markerCluster.addMarkers(rapeMarkers);
    markerCluster.addMarkers(robberyMarkers);
    markerCluster.addMarkers(assaultMarkers);
    markerCluster.addMarkers(burglaryMarkers);
    markerCluster.addMarkers(larcenyTheftMarkers);
    markerCluster.addMarkers(carTheftMarkers);

}



function initRequest() {
    if (window.XMLHttpRequest) {
        if (navigator.userAgent.indexOf('MSIE') != -1) {
            isIE = true;
        }
        return new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        isIE = true;
        return new ActiveXObject("Microsoft.XMLHTTP");
    }
}



